<?php

    /*********************************************************************************
     * LeafPHP error错误处理类 www.leafphp.com
     *-------------------------------------------------------------------------------
     * 错误处理类，主要用于捕获错误，禁止错误输出。让错误由此类处理
     * 有三种级别的错误处理：致命、警告、提示
     * 用于与core衔接。由core调用init之后，本类提供静态方法供系统使用错误处理机制
     * 注意：由于系统级的错误也交由框架处理，所以框架本身出错，可能会出现页面空白显示，出现这种情况，说明已发生致命错误，此时，在init设置error_reporting(-1)即可。
     * 为了最大限度的将错误交由框架处理，那么应最大限度的实现功能解耦合！
     *-------------------------------------------------------------------------------
     * $Author:hongshuaidong
     * $Dtime:2013-12-08
     ***********************************************************************************/

    if (!defined('__IS_LEAF__')) exit('Access Denied!');

    class error {

        //最近一次的系统错误
        static private $errorInfo=array();

        /**
         * 设置错误信息属性
         * @access private
         * @param array $errorInfo 错误消息 
         * @param string $errorForm 错误来源：是用户自定义错误还是系统级别错误
         */
        static private function setErrorInfo($errorInfo, $errorFrom){

            //如果系统正常结束，$errorInfo则为null，此时不可以参数函数运行，直接结束
            if( is_null($errorInfo) ) exit;
            self::$errorInfo      = $errorInfo;

        }

        /**
         * 错误处理初始化
         * 将因为错误导致的程序中断而无法捕获的错误，交由自定义函数捕获处理，同时，用户自定义的错误也由此处理
         */
        static public function init(){

            //设置错误显示
            ini_set('display_errors', true);
            //获取系统配置中的报错模式，如果该设置的不合法，设置为0
            $error_reporting_arr = array( E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, E_WARNING, E_NOTICE, 0, -1 );
            $error_reporting = intval( C('error_reporting') );
            $error_reporting = ( !in_array($error_reporting, $error_reporting_arr) ) ? -1 : $error_reporting;
            error_reporting( $error_reporting );
            
            //如果设置了error_sys_to_leaf为1，则代表将系统和用户错误交由框架处理
            if( C( 'error_sys_to_leaf' ) == true  ){
                //捕获用户自定义级别错误
                set_error_handler( 'error::catchUserError', E_ALL );
                //设置顶级异常处理器
                set_exception_handler( 'error::catchException' );
                //捕获系统级别错误
                register_shutdown_function( 'error::catchSysError' );
            }

        }

        /**
         * 获取用户自定义级别的错误，设置错误信息属性，调用统一处理方法
         * @access public
         * @param  int    $errno   接收的错误号
         * @param  string $srrstr  接收的错误信息
         * @param  string $errfile 接收的错误文件
         * @param  string $errline 接收的错误行号
         */
        static public function catchUserError($errno, $errstr, $errfile, $errline){

            //errorInfo与error_get_last获取的错误信息格式保持一致
            $errorInfo = array( 'type'=>$errno, 'message'=>$errstr, 'file'=>$errfile, 'line'=>$errline );
            self::setErrorInfo( $errorInfo ,'e_user' );
            self::catchError('e_user');

        }

        /**
         * 顶级异常处理器
         * @access public
         */
        static public function catchException($e){
            self::catchUserError( $e->getCode(), $e->getMessage(), $e->getFile(), $e->getLine() );
        }

        /**
         * 获取系统级别的错误，设置错误信息属性，调用统一处理方法
         */
        static public function catchSysError(){
            $errorInfo = error_get_last();
            self::setErrorInfo( $errorInfo, 'e_sys' );
            self::catchError('e_sys');
        }

        /**
         * 错误统一处理方法
         * 在脚本结束时，捕获错误信息，并处理
         * 此方法为public方法，只是因为init调用的时候，其内部函数的参数不能为self
         * @param string $level 错误类型，是用户自定义方式错误引用该函数，还是系统级别错误引用的该函数
         */
        static public function catchError($level){

            $errorInfo = self::$errorInfo;
            //打印错误
            //设置一个数组，判断错误type在没在数组中，在则写入日志信息，否则会出现脚本结束也写错误日志
            //设置严重错误的错误数组，0代表异常：$e->getCode的结果
            $error_array = array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, 0);
            //如果是严重错误，记录错误日志，并中断执行
            if(in_array($errorInfo['type'], $error_array)){
                //记录错误信息，目前是记录所有的错误信息
                if(C('error_log_record')==true){
                    self::errorRecord($errorInfo, 'leaf');
                }
                P($errorInfo);
                //模拟PHP自己错误退出，不能让致命错误发生后还可以继续运行
                die();
            }

        }

        /**
         * 错误日志记录
         * 记录用户级&系统级错误、网站错误等
         * @param array $errorInfo 错误信息
         * @param string $type 错误分类
         */
        static private function errorRecord($errorInfo ,$type='leaf'){

            //Log::errorLogRecord($errorInfo, $type='leaf');

        }

        /**
         * 抛出异常
         * @access public
         * @param  string $msg 要抛出的异常信息
         */
        static public function newException($msg){
            throw new Exception( $msg );
        }


    }

?>